package edu.kit.csl.cratylus.datatypes;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * A instance of this class stores a set of words. When used on the source
 * language side, entries correspond to real source language words. On the
 * target language side, however, we work with "word-labels", which may or may
 * not be used in the final target language vocabulary (depending on
 * {@link Word#isFixed()}). This class uses the GoF design pattern singleton.
 */
public class Vocabulary implements Iterable<Word> {

	/**
	 * Vocabulary identifier for the source language vocabulary.
	 */
	public static final int SRC_VOC = 0;
	
	/**
	 * Vocabulary identifier for the target language vocabulary. This should
	 * only be used for fixed words after MSA.
	 */
	public static final int TRGT_VOC = 1;
	
	/*
	 * Words stored as set
	 */
	private Map<String, Word> voc;
	
	/**
	 * Sole constructor. Creates an empty dictionary. This constructor is only
	 * public for testing purposes and maintaining extensibility. For access to
	 * the dictionaries of source and target language, please use
	 * {@link #getSingleton(int)}.
	 * 
	 * @see #getSingleton(int)
	 */
	public Vocabulary() {
		voc = new HashMap<String, Word>();
	}
	
	/*
	 * Singleton dictionary instances for both dictionaries (src and trgt).
	 */
	private static Vocabulary srcVoc, trgtVoc;
	
	/**
	 * Get the singleton instances (GoF lazy singleton) for the source or 
	 * target language.
	 * 
	 * @param voc the id of the vocabulary, which is stored as *_VOC constants.
	 * @return <code>null</code> if the id was not found, the requested 
	 * 		vocabulary otherwise
	 * @see #SRC_VOC
	 * @see #TRGT_VOC
	 */
	public static Vocabulary getSingleton(int dict) {
		switch (dict) {
		case SRC_VOC:
			if (srcVoc == null) {
				srcVoc = new Vocabulary();
			}
			return srcVoc;
		case TRGT_VOC:
			if (trgtVoc == null) {
				trgtVoc = new Vocabulary();
			}
			return trgtVoc;
		}
		return null;
	}
	
	/**
	 * Get the word by its string representation. If the word is unknown, 
	 * add it to the vocabulary as fixed word.
	 * 
	 * @param str string representation of the word
	 */
	public Word get(String str) {
		Word word = voc.get(str);
		if (word != null) {
			return word;
		}
		word = new Word(str, true);
		voc.put(str, word);
		return word;
	}
	
	/**
	 * Add a new word to the vocabulary. If the string representation already
	 * exists, override. Note that therefore homophones cannot be extracted
	 * properly.
	 * 
	 * @param word the new vocabulary entry
	 * @throws NullPointerException if <code>word</code> is null
	 */
	public void put(Word word) {
		voc.put(word.getContent(), word);
	}
	
	/**
	 * Add a new word to the vocabulary and associates it with the given 
	 * string. If the string already exists, override.
	 * 
	 * @param str the identification string
	 * @param word the new vocabulary entry
	 * @throws NullPointerException if <code>word</code> is null
	 */
	public void put(String str, Word word) {
		voc.put(str, word);
	}
	
	/**
	 * Remove all entries in the vocabulary. This may be useful in test
	 * scenarios.
	 */
	public void clear() {
		voc.clear();
	}

	@Override
	public Iterator<Word> iterator() {
		return voc.values().iterator();
	}
}
